001 /**
002 * Created by IntelliJ IDEA.
003 * User: Wei Wang
004 * Date: Nov 27, 2002
005 * Time: 3:50:11 PM
006 */
007
008 package EVolve.visualization;
009
010 import EVolve.data.*;
011 import EVolve.Scene;
012 import EVolve.visualization.VizFactory.VisualizationFactory;
013
014 import javax.swing.*;
015 import javax.imageio.*;
016 import java.awt.event.*;
017 import java.awt.image.BufferedImage;
018 import java.awt.*;
019 import java.util.*;
020 import java.io.*;
021
022 public abstract class Visualization implements Cloneable{
023 public static final int SELECT_TIME_FRAME = 0x0001;
024 public static final int SELECT_OCCURRED_ENTITIES = 0x0010;
025 public static final int SELECT_ALL_ENTITIES = 0x0100;
026 public static final int SELECT_X_AXIS = 0x1000;
027 public static final int SELECT_Y_AXIS = 0x0100;
028 public static final int SELECT_ALL_X_AXIS = 0x0010;
029 public static final int SELECT_ALL_Y_AXIS = 0x0001;
030
031
032 protected String name; // name of the visualization
033 protected EVolve.Window window; // window where the visualization is displayed
034
035 protected VisualizationDefinition definition; // definition of the visualization
036 protected ElementDefinition subjectDefinition; // definition of the subject
037
038 protected Dimension[] dimension; // dimension of the visualization
039 protected Component panel; // panel to draw the visualization
040
041 protected JDialog dialog; // configuration dialog
042 protected JPanel panelConfiguration; // configuration panel
043 protected JComboBox comboSubject; // subject selection
044 protected JComboBox[] comboDimension; // dimension selection
045 protected JTextField textName; // name input
046
047 protected JPopupMenu popup; // popup menu
048 private JMenu menuSort;
049 private JMenu[] menuDimension;
050 protected JMenuItem[][] itemSort;
051 private JMenuItem itemConfigure,itemRename;
052 private JMenuItem itemSelect,itemSave;
053 private JMenuItem itemClone;
054 private ReferenceDimension[] referenceDimension;
055 private int linkageId;
056 private VisualizationFactory factory = null;
057 private static int vizIDPool = 0;
058 protected int dataSourceId;
059
060 protected ElementDefinition[] elementDefinition; // definition of elements that can be used as subject
061 protected DataFilter[][][] dataFilter; // data filters that can be used by the dimensions
062 protected int mouseX, mouseY;
063
064 protected boolean freezed;
065
066 public Visualization() {
067 dimension = createDimension();
068 panel = createPanel();
069 panelConfiguration = null;
070 dialog = null;
071 linkageId = vizIDPool++;
072 addPopupTrigger(panel);
073 freezed = false;
074 }
075
076 public String getName() {
077 return name;
078 }
079
080 /**
081 * Sets the name of the visualization.
082 *
083 * @param name name of the visualization
084 */
085 public void setName(String name) {
086 this.name = name;
087 }
088
089 /**
090 * Sets the window of the visualization.
091 *
092 * @param window window of the visualization
093 */
094 public void setWindow(EVolve.Window window) {
095 this.window = window;
096 }
097
098 /**
099 * Gets the panel of the visualization.
100 *
101 * @return panel of the visualization
102 */
103 public Component getPanel() {
104 return panel;
105 }
106
107 /**
108 * Gets the additional configuration panel.
109 *
110 * @return the additional configuration panel, null if not necessary
111 */
112 protected JPanel createConfigurationPanel() {
113 return null; // by default, return null, each visualization can override this
114 }
115
116 protected void createMenu() {
117 popup = new JPopupMenu();
118
119 itemConfigure = new JMenuItem("Configure...");
120 itemConfigure.setMnemonic(KeyEvent.VK_C);
121 itemConfigure.addActionListener(new ActionListener() {
122 public void actionPerformed(ActionEvent e) {
123 configure();
124 }
125 });
126 popup.add(itemConfigure);
127
128 itemRename = new JMenuItem("Rename...");
129 itemRename.setMnemonic(KeyEvent.VK_R);
130 itemRename.addActionListener(new ActionListener() {
131 public void actionPerformed(ActionEvent e) {
132 String newName = JOptionPane.showInputDialog(Scene.getFrame(),"Enter a new name:",name);
133 if (newName != null) {
134 name = newName;
135 window.setTitle(name);
136 }
137 }
138 });
139 popup.add(itemRename);
140
141 itemSelect = new JMenuItem("Make Selection");
142 itemSelect.setMnemonic(KeyEvent.VK_M);
143 itemSelect.addActionListener(new ActionListener() {
144 public void actionPerformed(ActionEvent e) {
145 makeSelection();
146 }
147 });
148 popup.add(itemSelect);
149
150 menuSort = new JMenu("Sort");
151 menuSort.setMnemonic(KeyEvent.VK_S);
152 menuSort.setEnabled(false);
153 popup.add(menuSort);
154
155 ArrayList tempList = new ArrayList();
156 for (int i = 0; i < dimension.length; i++) {
157 if (dimension[i] instanceof ReferenceDimension) {
158 tempList.add(new Integer(i));
159 }
160 }
161
162 referenceDimension = new ReferenceDimension[tempList.size()];
163 menuDimension = new JMenu[tempList.size()];
164 itemSort = new JMenuItem[tempList.size()][];
165 for (int i = 0; i < tempList.size(); i++) {
166 int j = ((Integer)(tempList.get(i))).intValue();
167 referenceDimension[i] = (ReferenceDimension)(dimension[j]);
168 menuDimension[i] = new JMenu(definition.getDimensionDefinition()[j].getName());
169 menuSort.add(menuDimension[i]);
170 }
171
172 itemClone = new JMenuItem("Clone");
173 itemClone.setMnemonic(KeyEvent.VK_C);
174 itemClone.addActionListener(new ActionListener() {
175 public void actionPerformed(ActionEvent e) {
176 Scene.getVisualizationManager().cloneVisualization();
177 }
178 });
179 popup.add(itemClone);
180
181 itemSave = new JMenuItem("Save...");
182 itemSave.setMnemonic(KeyEvent.VK_V);
183 itemSave.addActionListener(new ActionListener() {
184 public void actionPerformed(ActionEvent e) {
185 save();
186 }
187 });
188 popup.add(itemSave);
189 }
190
191 private void showPopup(MouseEvent e) {
192 mouseX = e.getX();
193 mouseY = e.getY();
194 Rectangle rect = e.getComponent().getBounds();
195 Rectangle rect2 = popup.getBounds();
196 int posX = mouseX, posY = mouseY;
197 if (mouseY+rect2.height > rect.height)
198 posY = posY - rect2.height;
199 if (mouseX+rect2.width>rect.width)
200 posX = posX - rect2.width;
201 popup.show(e.getComponent(), posX, posY);
202 }
203
204 /**
205 * Sets the definition of the visualization, called by visualization manager after the visualization is created.
206 *
207 * @param definition definition of the visualization
208 */
209 public void setDefinition(VisualizationDefinition definition) {
210 panelConfiguration = createConfigurationPanel();
211
212 this.definition = definition;
213
214 elementDefinition = Scene.getDataManager().getElementDefinition(definition);
215
216 dataFilter = new DataFilter[elementDefinition.length][definition.getDimensionDefinition().length][];
217 for (int i = 0; i < dataFilter.length; i++) {
218 for (int j = 0; j < dataFilter[i].length; j++) {
219 dataFilter[i][j] = Scene.getDataManager().getDataFilter(elementDefinition[i],definition.getDimensionDefinition()[j].getProperty());
220 for (int k = 0; k < dataFilter[i][j].length; k++) {
221 assert (((dataFilter[i][j][k].getTargetType() != -1) && (dimension[j] instanceof ReferenceDimension)) || ((dataFilter[i][j][k].getTargetType() == -1) && (dimension[j] instanceof ValueDimension))) : "Incompatible dimension type.";
222 }
223 }
224 }
225
226 subjectDefinition = elementDefinition[0];
227 for (int i = 0; i < dimension.length; i++) {
228 dimension[i].setDataFilter(dataFilter[0][i][0]);
229 }
230
231 updateConfiguration();
232
233 createMenu();
234 }
235
236 /**
237 * Gets the definition of the subject.
238 *
239 * @return definition of the subject
240 */
241 public ElementDefinition getSubjectDefinition() {
242 return subjectDefinition;
243 }
244
245 /**
246 * Configures the visualization.
247 */
248 public void configure() {
249 if (dialog == null) {
250 createDialog();
251 }
252 dialog.pack();
253 Scene.getUIManager().showDialog(dialog, dialog.getWidth(), dialog.getHeight());
254 }
255
256 /**
257 * Save visualizations as disk file
258 */
259 public void save() {
260 Component target = panel;
261 if (target instanceof JScrollPane)
262 target = ((JScrollPane)panel).getViewport().getView();
263
264 BufferedImage image = new BufferedImage(target.getWidth(), target.getHeight(), BufferedImage.TYPE_INT_RGB);
265 target.paint(image.getGraphics());
266 ImageWriter writer = (ImageWriter)(ImageIO.getImageWritersByFormatName("png").next());
267
268 JFileChooser fc = new JFileChooser(Scene.getUIManager().getLastResultDir());
269 fc.setFileFilter(new javax.swing.filechooser.FileFilter() {
270 public String getDescription() {
271 return "PNG Files";
272 }
273
274 public boolean accept(File f) {
275 return f.getName().substring(f.getName().lastIndexOf('.') + 1).toLowerCase().equals("png");
276 }
277 });
278
279 if (fc.showSaveDialog(Scene.getFrame()) == JFileChooser.APPROVE_OPTION) {
280 File file = fc.getSelectedFile();
281 Scene.getUIManager().setLastResultDir(file.getPath());
282 try {
283 writer.setOutput(ImageIO.createImageOutputStream(file));
284 writer.write(image);
285 } catch (IOException e) {}
286 }
287 }
288
289 /**
290 * Creates the configuration dialog.
291 */
292 protected void createDialog() {
293 dialog = new JDialog(Scene.getFrame(), "Configure", true);
294
295 JPanel panelTitle = new JPanel(new FlowLayout());
296 dialog.getContentPane().add(panelTitle, BorderLayout.NORTH);
297
298 panelTitle.add(new JLabel("Title: "));
299
300 textName = new JTextField(name, 12);
301 panelTitle.add(textName);
302
303 JPanel panelMain = new JPanel(new BorderLayout());
304 panelMain.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Choose subject & dimensions"));
305 dialog.getContentPane().add(panelMain, BorderLayout.CENTER);
306
307 JPanel panelName = new JPanel(new GridLayout(definition.getDimensionDefinition().length + 1, 1, 5, 5));
308 panelMain.add(panelName, BorderLayout.WEST);
309
310 panelName.add(new JLabel(" Subject: "));
311 for (int i = 0; i < definition.getDimensionDefinition().length; i++) {
312 panelName.add(new JLabel(" " + definition.getDimensionDefinition()[i].getName() + ": "));
313 }
314
315 JPanel panelCombo = new JPanel(new GridLayout(definition.getDimensionDefinition().length + 1, 1, 5, 5));
316 panelMain.add(panelCombo, BorderLayout.CENTER);
317
318 comboSubject = new JComboBox();
319 for (int i = 0; i < elementDefinition.length; i++) {
320 comboSubject.addItem(elementDefinition[i].getName());
321 }
322 comboSubject.addActionListener(new ActionListener() {
323 public void actionPerformed(ActionEvent e) {
324 updateComboSubject();
325 }
326 });
327 panelCombo.add(comboSubject);
328
329 comboDimension = new JComboBox[definition.getDimensionDefinition().length];
330 for (int i = 0; i < comboDimension.length; i++) {
331 comboDimension[i] = new JComboBox();
332 comboDimension[i].addActionListener(new ActionListener() {
333 public void actionPerformed(ActionEvent e) {
334 updateComboDimension();
335 }
336 });
337 panelCombo.add(comboDimension[i]);
338 }
339
340 updateComboSubject();
341
342 if (panelConfiguration != null) {
343 panelMain.add(panelConfiguration, BorderLayout.SOUTH);
344 }
345
346 JPanel panelButton = new JPanel(new FlowLayout());
347 dialog.getContentPane().add(panelButton, BorderLayout.SOUTH);
348
349 JButton buttonApply = new JButton("Apply");
350 buttonApply.addActionListener(new ActionListener() {
351 public void actionPerformed(ActionEvent e) {
352 dialogApply();
353 }
354 });
355 panelButton.add(buttonApply);
356
357 JButton buttonCancel = new JButton("Cancel");
358 buttonCancel.addActionListener(new ActionListener() {
359 public void actionPerformed(ActionEvent e) {
360 dialogCancel();
361 }
362 });
363 panelButton.add(buttonCancel);
364
365 //*************
366 for (int i=0; i<comboSubject.getItemCount(); i++) {
367 if ((comboSubject.getItemAt(i)).equals(subjectDefinition.getName())) {
368 comboSubject.setSelectedIndex(i);
369 break;
370 }
371 }
372 for (int i=0;i<comboDimension.length;i++) {
373 for (int j =0 ; j<comboDimension[i].getItemCount();j++) {
374 if (comboDimension[i].getItemAt(j).equals(dimension[i].getDataFilter().getName())) {
375 comboDimension[i].setSelectedIndex(j);
376 break;
377 }
378 }
379 }
380 }
381
382 /**
383 * Updates the subject combo-box.
384 */
385 protected void updateComboSubject() {
386 comboSubject.setToolTipText(elementDefinition[comboSubject.getSelectedIndex()].getDescription());
387 for (int i = 0; i < comboDimension.length; i++) {
388 comboDimension[i].removeAllItems();
389 for (int j = 0; j < dataFilter[comboSubject.getSelectedIndex()][i].length; j++) {
390 comboDimension[i].addItem(dataFilter[comboSubject.getSelectedIndex()][i][j].getName());
391 }
392 }
393 updateComboDimension();
394
395 }
396
397 /**
398 * Updates the dimension combo-boxes.
399 */
400 protected void updateComboDimension() {
401 for (int i = 0; i < comboDimension.length; i++) {
402 if (comboDimension[i].getSelectedIndex() != -1) {
403 comboDimension[i].setToolTipText(dataFilter[comboSubject.getSelectedIndex()][i][comboDimension[i].getSelectedIndex()].getDescription());
404 }
405 }
406 }
407
408 /**
409 * Button "Apply" is clicked.
410 */
411 protected void dialogApply() {
412 dialog.setVisible(false);
413 subjectDefinition = elementDefinition[comboSubject.getSelectedIndex()];
414 name = textName.getText();
415 if (name.indexOf(subjectDefinition.getName()) == -1)
416 window.setTitle(name + " - " + subjectDefinition.getName());
417 else
418 window.setTitle(name);
419 reset();
420 updateConfiguration();
421 }
422
423 /**
424 * Button "Cancel" is clicked.
425 */
426 protected void dialogCancel() {
427 dialog.setVisible(false);
428 }
429
430 public void updateMenu() {
431 for (int i = 0; i < referenceDimension.length; i++) {
432 ArrayList comparatorList = referenceDimension[i].getComparator();
433 itemSort[i] = new JMenuItem[comparatorList.size()];
434 menuDimension[i].removeAll();
435 for (int j = 0; j < itemSort[i].length; j++) {
436 itemSort[i][j] = new JMenuItem(((EntityComparator)(comparatorList.get(j))).getName());
437 itemSort[i][j].addActionListener(new ActionListener() {
438 public void actionPerformed(ActionEvent e) {
439 selectComparator(e);
440 }
441 });
442 menuDimension[i].add(itemSort[i][j]);
443 }
444 }
445
446 ArrayList menuOptional = createOptionalMenu();
447 if (menuOptional == null ) return;
448 for (int i=0; i<menuOptional.size(); i++)
449 popup.add((JMenuItem)menuOptional.get(i));
450 }
451
452 protected ArrayList createOptionalMenu() {
453 return null;
454 }
455
456 public void selectComparator(ActionEvent e) {
457 for (int i = 0; i < itemSort.length; i++) {
458 for (int j = 0; j < itemSort[i].length; j++) {
459 if (itemSort[i][j] == e.getSource()) {
460 referenceDimension[i].selectComparator(j);
461 sort();
462 return;
463 }
464 }
465 }
466 }
467
468 protected void addPopupTrigger(Component component) {
469 Component target = component;
470 if (target instanceof JScrollPane)
471 target = ((JScrollPane)target).getViewport().getView();
472
473 target.addMouseListener(new MouseAdapter() {
474 public void mouseReleased(MouseEvent e) {
475 if (e.isPopupTrigger()) {
476 showPopup(e);
477 }
478 }
479
480 public void mousePressed(MouseEvent e) {
481 if (e.isPopupTrigger()) {
482 showPopup(e);
483 }
484 }
485 });
486 }
487
488 public void enableSortMenu() {
489 menuSort.setEnabled(true);
490 }
491
492 /**
493 * Creates the dimensions.
494 *
495 * @return the dimensions
496 */
497 public abstract Dimension[] createDimension();
498
499 /**
500 * Creates available selection menu items.
501 *
502 * @return the array of menu items
503 */
504 public abstract JMenuItem[] createSelectionMenuItem();
505
506 /**
507 * Creates the panel.
508 *
509 * @return the panel
510 */
511 protected abstract Component createPanel();
512
513 /**
514 * Updates the configuration.
515 */
516 protected abstract void updateConfiguration();
517
518 /**
519 * Gets ready for receiving data.
520 */
521 public abstract void preVisualize();
522
523 /**
524 * Receives data.
525 *
526 * @param data the data
527 */
528 protected abstract void receiveElement(Element element);
529
530 /**
531 * Visualizes after receiving data.
532 */
533 public abstract void visualize();
534
535 /**
536 * sorting the visualization with selected comparator
537 */
538 public abstract void sort();
539
540 /**
541 * according to user selectiong, get a data subset
542 */
543 public abstract void makeSelection();
544
545 /**
546 *
547 * @return a Hash map contains all current visualization's configure
548 */
549 public HashMap getCurrentConfigure() {
550 HashMap configure = new HashMap();
551
552 configure.put("Factory",getFactory());
553 configure.put("Subject",subjectDefinition);
554 configure.put("Name",name);
555 configure.put("Interval", new Integer(-1));
556 configure.put("Options", ";not using any options");
557
558 return configure;
559 }
560
561 /**
562 * following are all interfaces needed for predefined viz
563 */
564 public void autoUpdateConfiguration(HashMap config) {
565
566 int selectedSubjectId = -1;
567 Dimension [] predefinedDimension;
568
569 for (int i=0; i< elementDefinition.length; i++) {
570 if (elementDefinition[i].getName().equals(((ElementDefinition)config.get("Subject")).getName())) {
571 subjectDefinition = elementDefinition[i];
572 selectedSubjectId = i;
573 break;
574 }
575 }
576
577 predefinedDimension = (Dimension[])config.get("Dimension");
578 for (int i=0; i<dimension.length; i++) {
579 for (int j=0; j< dataFilter[selectedSubjectId][i].length;j++) {
580 if (dataFilter[selectedSubjectId][i][j].getName().equals(predefinedDimension[i].getDataFilter().getName())) {
581 dimension[i].setDataFilter(dataFilter[selectedSubjectId][i][j]);
582 break;
583 }
584 }
585 }
586
587 updateConfiguration();
588 window.setTitle(name);
589 menuSort.setEnabled(false);
590 }
591
592 public EVolve.Window getWindow() {
593 return window;
594 }
595
596 public void setFactory(VisualizationFactory factory) {
597 this.factory = factory;
598 }
599
600 public VisualizationFactory getFactory() {
601 return factory;
602 }
603
604 public void autoSave(String path, String dataFn) {
605 Component target = panel;
606 if (target instanceof JScrollPane)
607 target = ((JScrollPane)panel).getViewport().getView();
608
609 BufferedImage image = new BufferedImage(target.getWidth(), target.getHeight(), BufferedImage.TYPE_INT_RGB);
610 target.paint(image.getGraphics());
611 ImageWriter writer = (ImageWriter)(ImageIO.getImageWritersByFormatName("png").next());
612
613 try {
614 RandomAccessFile file = new RandomAccessFile((path+File.separator+
615 dataFn.substring(dataFn.lastIndexOf(File.separator)+1,dataFn.length()-4)+".png"),"rw");
616 writer.setOutput(ImageIO.createImageOutputStream(file));
617 writer.write(image);
618 } catch (IOException e) {
619 System.out.println("writing file error.");
620 }
621
622 }
623
624 /**
625 * following methods are used in unify/overlap viz
626 */
627 public abstract ReferenceDimension getLinkableDimension(int dim);
628
629 public int getVisualizationID() {
630 return linkageId;
631 }
632
633 public Dimension[] getDimension() {
634 return dimension;
635 }
636
637 public VisualizationDefinition getDefinition() {
638 return definition;
639 }
640
641 public long getxMax() {
642 return -1;
643 }
644
645 public AutoImage getImage() {
646 return null;
647 }
648
649 public void setImage(AutoImage image) {
650 return;
651 }
652
653 public void cleanup() {
654 dimension = null;
655 comboDimension = null;
656 menuDimension = null;
657 itemSort = null;
658 referenceDimension = null;
659 elementDefinition = null;
660 dataFilter = null;
661 }
662
663 protected int switchOption(boolean turn_on, int option, int add_on_option) {
664 int returnVal;
665 if (turn_on)
666 returnVal = option | add_on_option;
667 else
668 returnVal = option & ~add_on_option;
669 return returnVal;
670 }
671
672 public void setDataSourceId(int dataSourceId) {
673 this.dataSourceId = dataSourceId;
674 }
675
676 public int getDataSourceId() {
677 return dataSourceId;
678 }
679
680 protected void reset() {
681 for (int i = 0; i < dimension.length; i++) {
682 int selected = comboDimension[i].getSelectedIndex();
683 if (selected < dataFilter[comboSubject.getSelectedIndex()][i].length)
684 dimension[i].setDataFilter(dataFilter[comboSubject.getSelectedIndex()][i][selected]);
685 if (dimension[i] instanceof ReferenceDimension) ((ReferenceDimension)dimension[i]).clearEntityMap();
686 }
687 menuSort.setEnabled(false);
688 }
689
690 public boolean isFreezed() {
691 return freezed;
692 }
693
694 public Object clone() {
695 Visualization o = null;
696
697 try {
698 o = (Visualization)super.clone();
699 } catch (CloneNotSupportedException e) {
700 e.printStackTrace();
701 return null;
702 }
703
704 o.name = name + "- cloned";
705 o.definition = (VisualizationDefinition)definition.clone();
706 o.subjectDefinition = (ElementDefinition) subjectDefinition.clone();
707 o.dimension = new Dimension[dimension.length];
708 o.factory = (VisualizationFactory)factory.clone();
709 o.elementDefinition = new ElementDefinition[elementDefinition.length];
710 for (int i=0; i<elementDefinition.length; i++)
711 o.elementDefinition[i] = (ElementDefinition)elementDefinition[i].clone();
712 o.dataFilter = new DataFilter[dataFilter.length][][];
713 o.linkageId = vizIDPool++;
714 for (int i=0; i<dataFilter.length; i++) {
715 o.dataFilter[i] = new DataFilter[dataFilter[i].length][];
716 for (int j=0; j<dataFilter[i].length; j++) {
717 o.dataFilter[i][j] = new DataFilter[dataFilter[i][j].length];
718 for (int k=0; k<dataFilter[i][j].length; k++)
719 o.dataFilter[i][j][k] = (DataFilter)dataFilter[i][j][k].clone();
720 }
721 }
722 return o;
723 }
724 }